﻿using System;
using System.Collections.Generic;
using Core.Infrastructure.AnimationService;
using Core.Infrastructure.Constants;
using Core.Infrastructure.EventArgs;
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Presentation.Events;
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;
using System.Windows.Controls;

namespace Core.Infrastructure.ScreenManager
{
    /// <summary>
    /// responsible for conducting screens on master page
    /// </summary>
    public class ScreenManager
    {
        #region Properties

        protected IScreenFactoryRegistry ScreenFactoryRegistry { get; set; }
        public IAnimationService AnimationService { get; set; }
        protected IEventAggregator EventAggregator { get; set; }
        protected IDictionary<ScreenType, IScreen> ScreenCollection { get; set; }

        #endregion

        #region Fields

        private IViewInjector viewInjector;
        private bool isScreenDeactivating = false;
        private IDictionary<string, ScreenType> regionActiveScreen;

        #endregion

        #region Constructors

        public ScreenManager(IScreenFactoryRegistry screenFactoryRegistry,
            IEventAggregator eventAggregator,
            IViewInjector viewInjector,
            IAnimationService animationService)
        {
            this.ScreenFactoryRegistry = screenFactoryRegistry;
            this.AnimationService = animationService;
            this.EventAggregator = eventAggregator;
            this.viewInjector = viewInjector;

            this.ScreenCollection = new Dictionary<ScreenType, IScreen>();
            this.regionActiveScreen = new Dictionary<string, ScreenType>
                                          {
                                              {RegionConstants.RegionMainArea, ScreenType.Empty}
                                          };
            SubscribeToEvents();
        }

        #endregion

        #region Private Methods

        private void SubscribeToEvents()
        {
            this.EventAggregator.GetEvent<ScreenActivateEvent>().Subscribe(
                    ActivateScreen,
                    ThreadOption.UIThread,
                    true,
                    IsNotificationRelevant);
        }

        private bool IsNotificationRelevant(ScreenEventArgs args)
        {
            //TODO
            return true;
        }

        private bool IsThereActiveScreen(ScreenType activeScreen)
        {
            return activeScreen != ScreenType.Empty && this.ScreenCollection.ContainsKey(activeScreen);
        }

        #endregion

        #region Public Methods

        public void ActivateScreen(ScreenEventArgs args)
        {
            // There is no such registered screen type
            if (!this.ScreenFactoryRegistry.IsRegistered(args.ScreenType))
                return;

            if (this.isScreenDeactivating)
                return;

            ScreenType activeScreenType = this.regionActiveScreen[args.RegionName];
            // Check if an active screen exists. 
            if (IsThereActiveScreen(activeScreenType))
            {
                IScreen currentActive = this.ScreenCollection[activeScreenType];

                if (currentActive.CanLeave())
                {
                    LeaveScreen(args, currentActive);
                }
                else
                {
                    currentActive.CleanUp(); //TODO: implement CleanUp
                }
            }
            else
            {
                ShowScreen(args);
            }
        }

        public void LeaveScreen(ScreenEventArgs args, IScreen screen)
        {
            if (args.PerformAnimation)
            {
                this.AnimationService.LeaveUIElementAnimation(() => SwitchScreens(args, screen));
            }
            else
            {
                SwitchScreens(args, screen);
            }
        }

        public void SwitchScreens(ScreenEventArgs newScreenArgs, IScreen oldScreen)
        {
            oldScreen.View.Visibility = 0;
            viewInjector.RemoveAndDeactivateIfShould(newScreenArgs.RegionName, oldScreen.View);
            ShowScreen(newScreenArgs);
        }

        public void ShowScreen(ScreenEventArgs newScreenArgs)
        {
            if (String.IsNullOrEmpty(newScreenArgs.RegionName))
            {
                throw new ArgumentNullException("regionName null or empty");
            }

            IScreen screen = CreateScreenIfNeccessary(newScreenArgs.ScreenType);

            screen.RecreateView();
            viewInjector.InjectView(newScreenArgs.RegionName, screen.View);
            this.regionActiveScreen[newScreenArgs.RegionName] = newScreenArgs.ScreenType;

            if (newScreenArgs.PerformAnimation)
            {
                this.AnimationService.EnterUIElementAnimation(() => 
                {
                    screen.Activate(newScreenArgs);
                });
            }
            else
            {
                screen.Activate(newScreenArgs);
            }
        }

        public IScreen CreateScreenIfNeccessary(ScreenType screenKey)
        {
            IScreen screen = null;
            // use the screen type to see if the screen exists in the collection
            if (ScreenCollection.ContainsKey(screenKey))
            {
                screen = ScreenCollection[screenKey];
            }
            else // if it does not, then use the screen type to get the factory that is made for creating that type of screen and make it, add to collection
            {
                if (this.ScreenFactoryRegistry.IsRegistered(screenKey))
                {
                    screen = this.ScreenFactoryRegistry.Get(screenKey).CreateScreen();
                    ScreenCollection.Add(screenKey, screen);
                }
                else
                {
                    throw new ArgumentOutOfRangeException("Screen not found");
                }
            }
            return screen;
        } 

        #endregion
    }
}
